<template>
  <div>
    <span>
      <Tag
        :style="{ marginLeft: '10px', marginTop: '15px' }"
        v-for="service in serviceList"
        :key="service.serviceCode"
        :color="service.serviceColor"
        >{{ service.serviceCode }}
        <template #icon>
          <check-circle-outlined />
        </template>
      </Tag>
    </span>
    <div id="container"></div>
  </div>
</template>
<script lang="ts" setup>
import { Tag } from 'ant-design-vue';
import { CheckCircleOutlined } from '@ant-design/icons-vue';
import { ref } from 'vue';
import G6, { Graph } from '@antv/g6';
import { TraceItem } from '/@/api/maintenance-center/skywalking/trace/model/traceModel';
interface ServiceObj {
  serviceCode: string;
  serviceColor: string;
}
const serviceList = ref<ServiceObj[]>([]);
const color = ['#69b1ff', '#52c41a', '#fa8c16', '#b37feb', '#85a5ff', '#ffadd2', '#fadb14', '#1d39c4'];
const spansInfo = ref<TraceItem[]>([]);
let graph: Graph;
let serviceCodeList: string[] = [];
const emit = defineEmits(['getDetail']);
interface Node {
  id: string;
  label: string;
  x: number;
  y: number;
  style: object;
  daughter: string[];
  size?: number[];
  type?: string;
  endpointTooltip?: string;
}
let nodes: Node[] = [];
interface Edge {
  source: string;
  target: string;
}
let edges: Edge[] = [];
let canvasHight = 100;
const customNodeConf = {
  shape: 'circle',
  size: [20],
  style: {
    lineWidth: 3,
  },
  labelCfg: {
    position: 'right',
    style: {
      fill: 'black',
      fontSize: 15,
    },
  },
};
const customEdgeConf = {
  type: 'cubic-vertical',
  style: {
    stroke: '#e2e2e2',
  },
};
const customModeConf = {
  default: [
    'drag-canvas',
    'zoom-canvas',
    {
      type: 'tooltip',
      formatText(model) {
        if (model.id.substring(0, 9) == 'duration-') {
          return '持续时间：' + model.label;
        } else if (model.label) {
          return model.endpointTooltip;
        }
        return '初始';
      },
      offset: 150,
    },
  ],
};
// 藏匿超长label
const ellipsisEndpoint = (endpointName) => {
  if (endpointName.length > 70) {
    return (endpointName = endpointName.substring(0, 70) + '...');
  }
  return endpointName;
};
// node的xy轴算法
let incrementy = 80;
let incrementx = 50;
function getAxis(spansInfo) {
  let k = 1;
  spansInfo.forEach((item) => {
    let nodeColor = serviceList.value[serviceCodeList.indexOf(item.serviceCode)].serviceColor;
    item.y = k++ * incrementy;
    item.x = 0;
    if (item.refs.length == 0 && item.spanId == 0 && item.parentSpanId == -1) {
      item.x = 100;
    } else if (item.refs.length == 0 && item.parentSpanId !== -1) {
      spansInfo.forEach((it) => {
        if (it.segmentId == item.segmentId && it.spanId == item.parentSpanId) {
          item.x = it.x + incrementx;
          it.daughter.push(item.nodeId);
        }
      });
    } else if (item.refs.length !== 0) {
      spansInfo.forEach((it) => {
        if (it.segmentId == item.refs[0].parentSegmentId && it.spanId == item.refs[0].parentSpanId) {
          item.x = it.x + incrementx;
          it.daughter.push(item.nodeId);
        }
      });
    }
    item.x = item.x == 0 ? 100 : item.x;
    let nodeItem = {
      id: item.nodeId,
      label: ellipsisEndpoint(item.endpointName) + '\n   ' + item.component + '-' + item.layer,
      x: item.x,
      y: item.y,
      style: { stroke: nodeColor, cursor: 'pointer' },
      labelCfg: { style: { cursor: 'pointer' } },
      daughter: item.daughter,
      endpointTooltip: item.endpointName,
    };
    let durationItem = {
      type: 'rect',
      id: 'duration-' + item.nodeId,
      label: item.endTime - item.startTime + ' ms',
      x: 700 + ((item.endTime - item.startTime) * 10) / 2,
      y: item.y,
      size: [(item.endTime - item.startTime) * 10, 10],
      style: { stroke: nodeColor, cursor: 'pointer' },
      labelCfg: { style: { cursor: 'pointer' } },
      daughter: [],
    };
    nodes.push(nodeItem);
    nodes.push(durationItem);
  });
}

const getEdges = () => {
  edges = [];
  canvasHight = nodes[nodes.length - 1].y + 100;
  nodes.forEach((item) => {
    if (item.daughter.length != 0) {
      item.daughter.forEach((i) => {
        edges.push({ source: item.id, target: i });
      });
    }
  });
};
// 接收spans
function setChart(spans) {
  spansInfo.value = spans;
  spansInfo.value.forEach((item) => {
    serviceCodeList.push(item.serviceCode);
  });
  serviceCodeList = Array.from(new Set(serviceCodeList));
  serviceCodeList.forEach((it) => {
    serviceList.value.push({ serviceCode: it, serviceColor: color[serviceCodeList.indexOf(it)] });
  });
  nodes = [
    {
      id: 'node1',
      label: '',
      x: 50,
      y: 50,
      style: {},
      daughter: [spansInfo.value[0].nodeId],
    },
  ];
  getAxis(spansInfo.value);
  getEdges();
  // 绘图
  const data = {
    nodes,
    edges,
  };
  graph = new G6.Graph({
    container: 'container',
    width: 1450,
    height: canvasHight,
    defaultNode: customNodeConf,
    defaultEdge: customEdgeConf,
    modes: customModeConf,
  });
  graph.on('node:click', (evt) => {
    if (evt.item?._cfg?.id !== 'node1') {
      emit('getDetail', evt.item?._cfg?.id);
    }
  });
  graph.data(data);
  graph.render();
}
const clearChart = () => {
  serviceCodeList = [];
  serviceList.value = [];
  if (graph) {
    graph.destroy();
  }
};
defineExpose({ setChart, clearChart });
</script>
<style>
.g6-tooltip {
  padding: 10px 6px;
  color: #fff !important;
  background-color: rgb(61, 61, 61) !important;
  border: 1px solid #e2e2e2;
  border-radius: 4px;
}
</style>
